<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>正则表达式入门 | Guru Note</title>
    <meta name="generator" content="VuePress 1.7.1">
    <link rel="stylesheet" href="./font/index.css">
    <script src="/docs/icon/svg.js"></script>
    <meta name="description" content="Sea of dawn">
    
    <link rel="preload" href="/docs/assets/css/0.styles.efa081cd.css" as="style"><link rel="preload" href="/docs/assets/js/app.cdec4db9.js" as="script"><link rel="preload" href="/docs/assets/js/2.993bd611.js" as="script"><link rel="preload" href="/docs/assets/js/9.2fa568cb.js" as="script"><link rel="preload" href="/docs/assets/js/3.a7f8dd77.js" as="script"><link rel="preload" href="/docs/assets/js/7.5a12abef.js" as="script"><link rel="prefetch" href="/docs/assets/js/10.c3177bdb.js"><link rel="prefetch" href="/docs/assets/js/100.60c23478.js"><link rel="prefetch" href="/docs/assets/js/101.7ea29e1b.js"><link rel="prefetch" href="/docs/assets/js/102.7f47e485.js"><link rel="prefetch" href="/docs/assets/js/103.ea9ce400.js"><link rel="prefetch" href="/docs/assets/js/104.4ec6a518.js"><link rel="prefetch" href="/docs/assets/js/105.75526347.js"><link rel="prefetch" href="/docs/assets/js/106.01f6b03b.js"><link rel="prefetch" href="/docs/assets/js/107.69fe0811.js"><link rel="prefetch" href="/docs/assets/js/108.fa23768b.js"><link rel="prefetch" href="/docs/assets/js/109.90df1698.js"><link rel="prefetch" href="/docs/assets/js/11.54347528.js"><link rel="prefetch" href="/docs/assets/js/110.dd8d7227.js"><link rel="prefetch" href="/docs/assets/js/111.ccf25ceb.js"><link rel="prefetch" href="/docs/assets/js/112.6963298f.js"><link rel="prefetch" href="/docs/assets/js/113.30ceb3d8.js"><link rel="prefetch" href="/docs/assets/js/114.42ef6603.js"><link rel="prefetch" href="/docs/assets/js/115.f1db7817.js"><link rel="prefetch" href="/docs/assets/js/116.92971223.js"><link rel="prefetch" href="/docs/assets/js/117.c5a65e7e.js"><link rel="prefetch" href="/docs/assets/js/118.e329035d.js"><link rel="prefetch" href="/docs/assets/js/119.cbb17c5e.js"><link rel="prefetch" href="/docs/assets/js/12.c8144ee8.js"><link rel="prefetch" href="/docs/assets/js/120.8537f6a6.js"><link rel="prefetch" href="/docs/assets/js/121.257d3851.js"><link rel="prefetch" href="/docs/assets/js/122.96a5f921.js"><link rel="prefetch" href="/docs/assets/js/123.2220fd12.js"><link rel="prefetch" href="/docs/assets/js/124.552b1a29.js"><link rel="prefetch" href="/docs/assets/js/125.4e36fe37.js"><link rel="prefetch" href="/docs/assets/js/126.3939cdcc.js"><link rel="prefetch" href="/docs/assets/js/127.014df434.js"><link rel="prefetch" href="/docs/assets/js/13.edc237a8.js"><link rel="prefetch" href="/docs/assets/js/14.66270d4f.js"><link rel="prefetch" href="/docs/assets/js/15.0804164b.js"><link rel="prefetch" href="/docs/assets/js/16.dec928ab.js"><link rel="prefetch" href="/docs/assets/js/17.bbfc90a8.js"><link rel="prefetch" href="/docs/assets/js/18.76904860.js"><link rel="prefetch" href="/docs/assets/js/19.80a98011.js"><link rel="prefetch" href="/docs/assets/js/20.c39e42bd.js"><link rel="prefetch" href="/docs/assets/js/21.287b744a.js"><link rel="prefetch" href="/docs/assets/js/22.c9506be7.js"><link rel="prefetch" href="/docs/assets/js/23.9732a229.js"><link rel="prefetch" href="/docs/assets/js/24.21ed1f05.js"><link rel="prefetch" href="/docs/assets/js/25.8f1926f6.js"><link rel="prefetch" href="/docs/assets/js/26.61b6eb9f.js"><link rel="prefetch" href="/docs/assets/js/27.3706753c.js"><link rel="prefetch" href="/docs/assets/js/28.b6b182cf.js"><link rel="prefetch" href="/docs/assets/js/29.ae979ad9.js"><link rel="prefetch" href="/docs/assets/js/30.f2233269.js"><link rel="prefetch" href="/docs/assets/js/31.06780314.js"><link rel="prefetch" href="/docs/assets/js/32.2dad91d1.js"><link rel="prefetch" href="/docs/assets/js/33.87ec6e22.js"><link rel="prefetch" href="/docs/assets/js/34.7b75f220.js"><link rel="prefetch" href="/docs/assets/js/35.3184991f.js"><link rel="prefetch" href="/docs/assets/js/36.28248fbe.js"><link rel="prefetch" href="/docs/assets/js/37.5824a979.js"><link rel="prefetch" href="/docs/assets/js/38.99a364fe.js"><link rel="prefetch" href="/docs/assets/js/39.a5f43732.js"><link rel="prefetch" href="/docs/assets/js/4.a55d89ed.js"><link rel="prefetch" href="/docs/assets/js/40.629f78f7.js"><link rel="prefetch" href="/docs/assets/js/41.e4f58d1c.js"><link rel="prefetch" href="/docs/assets/js/42.9f41aa47.js"><link rel="prefetch" href="/docs/assets/js/43.636412b6.js"><link rel="prefetch" href="/docs/assets/js/44.3b491aef.js"><link rel="prefetch" href="/docs/assets/js/45.77df19bc.js"><link rel="prefetch" href="/docs/assets/js/46.01f53ddd.js"><link rel="prefetch" href="/docs/assets/js/47.476e85c9.js"><link rel="prefetch" href="/docs/assets/js/48.198502dc.js"><link rel="prefetch" href="/docs/assets/js/49.0d59d332.js"><link rel="prefetch" href="/docs/assets/js/5.1597c0f8.js"><link rel="prefetch" href="/docs/assets/js/50.02baf101.js"><link rel="prefetch" href="/docs/assets/js/51.c9fbd54d.js"><link rel="prefetch" href="/docs/assets/js/52.4cb4459b.js"><link rel="prefetch" href="/docs/assets/js/53.248450d7.js"><link rel="prefetch" href="/docs/assets/js/54.83f12d1e.js"><link rel="prefetch" href="/docs/assets/js/55.b18accba.js"><link rel="prefetch" href="/docs/assets/js/56.8a160b09.js"><link rel="prefetch" href="/docs/assets/js/57.b854a940.js"><link rel="prefetch" href="/docs/assets/js/58.4500f315.js"><link rel="prefetch" href="/docs/assets/js/59.59400e36.js"><link rel="prefetch" href="/docs/assets/js/6.887ba020.js"><link rel="prefetch" href="/docs/assets/js/60.4dd5b5bb.js"><link rel="prefetch" href="/docs/assets/js/61.50d8c8f6.js"><link rel="prefetch" href="/docs/assets/js/62.17d10daa.js"><link rel="prefetch" href="/docs/assets/js/63.d5f821cc.js"><link rel="prefetch" href="/docs/assets/js/64.7bf2519f.js"><link rel="prefetch" href="/docs/assets/js/65.0a1c9bc8.js"><link rel="prefetch" href="/docs/assets/js/66.1bcaa81c.js"><link rel="prefetch" href="/docs/assets/js/67.326bdf9b.js"><link rel="prefetch" href="/docs/assets/js/68.2b3b63b2.js"><link rel="prefetch" href="/docs/assets/js/69.c9b1a1a9.js"><link rel="prefetch" href="/docs/assets/js/70.a5fede78.js"><link rel="prefetch" href="/docs/assets/js/71.288d9643.js"><link rel="prefetch" href="/docs/assets/js/72.8665d6b8.js"><link rel="prefetch" href="/docs/assets/js/73.47155429.js"><link rel="prefetch" href="/docs/assets/js/74.4d4738f8.js"><link rel="prefetch" href="/docs/assets/js/75.659a325f.js"><link rel="prefetch" href="/docs/assets/js/76.a263f692.js"><link rel="prefetch" href="/docs/assets/js/77.9d86d59e.js"><link rel="prefetch" href="/docs/assets/js/78.a41b5d88.js"><link rel="prefetch" href="/docs/assets/js/79.5d0fc4cc.js"><link rel="prefetch" href="/docs/assets/js/8.80d0c81d.js"><link rel="prefetch" href="/docs/assets/js/80.01a11f6a.js"><link rel="prefetch" href="/docs/assets/js/81.9c0207d9.js"><link rel="prefetch" href="/docs/assets/js/82.12be8e04.js"><link rel="prefetch" href="/docs/assets/js/83.00fa5f46.js"><link rel="prefetch" href="/docs/assets/js/84.f14cd53c.js"><link rel="prefetch" href="/docs/assets/js/85.e9d7c0d1.js"><link rel="prefetch" href="/docs/assets/js/86.eb78fbbf.js"><link rel="prefetch" href="/docs/assets/js/87.8a31f5ae.js"><link rel="prefetch" href="/docs/assets/js/88.0f10e6dc.js"><link rel="prefetch" href="/docs/assets/js/89.a17253c9.js"><link rel="prefetch" href="/docs/assets/js/90.e56bc609.js"><link rel="prefetch" href="/docs/assets/js/91.5277db18.js"><link rel="prefetch" href="/docs/assets/js/92.b0bb8aa7.js"><link rel="prefetch" href="/docs/assets/js/93.cd790b77.js"><link rel="prefetch" href="/docs/assets/js/94.76bbe227.js"><link rel="prefetch" href="/docs/assets/js/95.4f07ca32.js"><link rel="prefetch" href="/docs/assets/js/96.2b1e8485.js"><link rel="prefetch" href="/docs/assets/js/97.e93b3461.js"><link rel="prefetch" href="/docs/assets/js/98.658063a1.js"><link rel="prefetch" href="/docs/assets/js/99.a3642e6d.js">
    <link rel="stylesheet" href="/docs/assets/css/0.styles.efa081cd.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container custom-page-regex"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/docs/" class="home-link router-link-active"><!----> <span class="site-name">Guru Note</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----> <svg aria-hidden="true" class="icon search-icon"><use xlink:href="#icon-search"></use></svg></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/docs/layout/" class="nav-link">
  CSS 手册
</a></div><div class="nav-item"><a href="/docs/js/" class="nav-link">
  JavaScript
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端进阶" class="dropdown-title"><span class="title">前端进阶</span> <span class="arrow down"></span></button> <button type="button" aria-label="前端进阶" class="mobile-dropdown-title"><span class="title">前端进阶</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>
          前端框架
        </h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/docs/vue/" class="nav-link">
  Vue.js
</a></li><li class="dropdown-subitem"><a href="/docs/react/" class="nav-link">
  React.js
</a></li></ul></li><li class="dropdown-item"><h4>
          拓展语言
        </h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/docs/node/" class="nav-link">
  Node.js
</a></li><li class="dropdown-subitem"><a href="/docs/ts/" class="nav-link">
  TypeScript
</a></li></ul></li></ul></div></div><div class="nav-item"><a href="/docs/devbook/" class="nav-link router-link-active">
  开发手册
</a></div><div class="nav-item"><a href="/docs/exam/" class="nav-link">
  金九银十
</a></div><div class="nav-item"><a href="/docs/message/" class="nav-link">
  留言板
</a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><div class="el-scrollbar"><div class="scrollbar-wrapper el-scrollbar__wrap el-scrollbar__wrap--hidden-default"><div class="el-scrollbar__view"><nav class="nav-links"><div class="nav-item"><a href="/docs/layout/" class="nav-link">
  CSS 手册
</a></div><div class="nav-item"><a href="/docs/js/" class="nav-link">
  JavaScript
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端进阶" class="dropdown-title"><span class="title">前端进阶</span> <span class="arrow down"></span></button> <button type="button" aria-label="前端进阶" class="mobile-dropdown-title"><span class="title">前端进阶</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>
          前端框架
        </h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/docs/vue/" class="nav-link">
  Vue.js
</a></li><li class="dropdown-subitem"><a href="/docs/react/" class="nav-link">
  React.js
</a></li></ul></li><li class="dropdown-item"><h4>
          拓展语言
        </h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/docs/node/" class="nav-link">
  Node.js
</a></li><li class="dropdown-subitem"><a href="/docs/ts/" class="nav-link">
  TypeScript
</a></li></ul></li></ul></div></div><div class="nav-item"><a href="/docs/devbook/" class="nav-link router-link-active">
  开发手册
</a></div><div class="nav-item"><a href="/docs/exam/" class="nav-link">
  金九银十
</a></div><div class="nav-item"><a href="/docs/message/" class="nav-link">
  留言板
</a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/docs/devbook/" aria-current="page" class="sidebar-link">介绍</a></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>开发者小记</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/docs/devbook/note/regex-getting-started.html" aria-current="page" class="active sidebar-link">正则表达式入门</a></li><li><a href="/docs/devbook/note/lisp-getting-started.html" class="sidebar-link">Lisp 入门</a></li><li><a href="/docs/devbook/note/search-skill.html" class="sidebar-link">搜索引擎常用技巧</a></li><li><a href="/docs/devbook/note/the-http.html" class="sidebar-link">HTTP 相关知识科普</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>环境搭建</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/docs/devbook/setting/node-use.html" class="sidebar-link">Node.js 安装教程</a></li><li><a href="/docs/devbook/setting/cmder-install.html" class="sidebar-link">Cmder 配置教程</a></li><li><a href="/docs/devbook/setting/vscode-and-cmder-use.html" class="sidebar-link">VSCode 与 Cmder 配合使用</a></li><li><a href="/docs/devbook/setting/tool-recommend.html" class="sidebar-link">Windows 常用工具</a></li><li><a href="/docs/devbook/setting/webstorm.html" class="sidebar-link">WebStorm 配置</a></li><li><a href="/docs/devbook/setting/vscode.html" class="sidebar-link">VSCode 配置</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>命令行工具</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/docs/devbook/cli-plugin.html" class="sidebar-link">命令行常用插件</a></li><li><a href="/docs/devbook/cli-hot-key.html" class="sidebar-link">命令行快捷键</a></li><li><a href="/docs/devbook/git-getting-started.html" class="sidebar-link">Git 命令入门</a></li><li><a href="/docs/devbook/git-many-remotely.html" class="sidebar-link">Git 关联多个远程仓库</a></li><li><a href="/docs/devbook/mobile-file.html" class="sidebar-link">文件操作命令</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>博客搭建</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/docs/devbook/hexo-build-blog.html" class="sidebar-link">Hexo 搭建博客教程</a></li><li><a href="/docs/devbook/hexo-generate.html" class="sidebar-link">Hexo 生成永久链接</a></li><li><a href="/docs/devbook/hexo-theme.html" class="sidebar-link">Hexo 主题推荐</a></li><li><a href="/docs/devbook/hugo-created-blog.html" class="sidebar-link">Hugo 搭建个人博客</a></li></ul></section></li></ul> </div></div><div class="el-scrollbar__bar is-horizontal"><div class="el-scrollbar__thumb" style="width:0;transform:translateX(0%);ms-transform:translateX(0%);webkit-transform:translateX(0%);"></div></div><div class="el-scrollbar__bar is-vertical"><div class="el-scrollbar__thumb" style="height:0;transform:translateY(0%);ms-transform:translateY(0%);webkit-transform:translateY(0%);"></div></div></div></aside> <main class="page"> <div class="re-page-top"><h1 class="title"><span>正则表达式入门</span></h1> <div class="re-page-index"><div class="page-index-title"><span class="open-catalog"><svg aria-hidden="true" class="icon arrow-right"><use xlink:href="#icon-bold-right"></use></svg> <span class="text">目录</span></span></div> <div class="page-index-content"><div class="page-catalog" style="display:none;"><div class="synopsis-wrap"><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#相关链接" class="synopsis-link">相关链接</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#入门" class="synopsis-link">入门</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#元字符" class="synopsis-link">元字符</a><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#常用的元字符" class="synopsis-link">常用的元字符</a></li></ul></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#字符转义" class="synopsis-link">字符转义</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#重复" class="synopsis-link">重复</a><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#常用的限定符" class="synopsis-link">常用的限定符</a></li></ul></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#字符类" class="synopsis-link">字符类</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#分枝条件" class="synopsis-link">分枝条件</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#分组" class="synopsis-link">分组</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#反义" class="synopsis-link">反义</a><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#常用的反义代码" class="synopsis-link">常用的反义代码</a></li></ul></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#后向引用" class="synopsis-link">后向引用</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#零宽断言" class="synopsis-link">零宽断言</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#负向零宽断言" class="synopsis-link">负向零宽断言</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#注释" class="synopsis-link">注释</a></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#贪婪与懒惰" class="synopsis-link">贪婪与懒惰</a><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#懒惰限定符" class="synopsis-link">懒惰限定符</a></li></ul></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#处理选项" class="synopsis-link">处理选项</a><ul class="synopsis-ul"><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#常用的处理选项" class="synopsis-link">常用的处理选项</a></li></ul></li><li class="synopsis-li"><a href="/docs/devbook/note/regex-getting-started.html#平衡组-递归匹配" class="synopsis-link">平衡组/递归匹配</a></li></ul></div></div></div></div></div> <div class="theme-default-content content__default"><h2 id="相关链接"><a href="#相关链接" class="header-anchor">#</a> 相关链接</h2> <ul><li>内容参考：<a href="https://deerchao.cn/tutorials/regex/regex.htm" target="_blank" rel="noopener noreferrer">正则表达式30分钟入门教程<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li>正则验证：<a href="https://deerchao.cn/tools/wegester/" target="_blank" rel="noopener noreferrer">在线正则验证器(JavaScript)<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h2 id="入门"><a href="#入门" class="header-anchor">#</a> 入门</h2> <p><code>\b</code> 是正则表达式规定的一个特殊代码（元字符），代表着单词的开头或结尾，也就是单词的分界处。</p> <blockquote><p>如果需要更精确的说法，<code>\b</code> 匹配这样的位置：它的前一个字符和后一个字符不全是（一个是,一个不是或不存在）<code>\w</code>。</p></blockquote> <p>假如要精确地查找 hi 这个单词，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>him, hi, history, high</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\bhi\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>him, <span class="code-active">hi</span>, history, high</code></pre></div></section></div> <p>假如要找 hi 后面不远处跟着的一个 Lucy 的内容，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>him, hi, reset, Lucy, high</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\bhi\b.*\bLucy\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>him, <span class="code-active">hi, reset, Lucy</span>, high</code></pre></div></section></div> <p><code>.</code> 是另一个元字符，匹配<strong>除了换行符以外的任意字符</strong>。</p> <p><code>*</code> 同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定 <code>*</code> <strong>前边的内容可以连续重复使用任意次以使整个表达式得到匹配</strong>。</p> <p>将元字符与其他字符组合起来，就可以组成更强大的正则表达式，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>012-34567890
123-45678901</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>0\d\d-\d\d\d\d\d\d\d\d</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">012-34567890</span>
123-45678901</code></pre></div></section></div> <p>这里的 <code>\d</code> 是个新的元字符，匹配一位数字（0，或1，或2，或…）。</p> <p>为了避免多次重复，可以使用 <code>{}</code> 简写上面的正则，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>012-34567890
123-45678901</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>0\d{2}-\d{8}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">012-34567890</span>
123-45678901</code></pre></div></section></div> <h2 id="元字符"><a href="#元字符" class="header-anchor">#</a> 元字符</h2> <p>现在你已经知道几个有用的元字符了（<code>\b</code>、<code>.</code>、<code>*</code>，还有 <code>\d</code>），正则表达式还有很多元字符，如：</p> <ul><li><code>\s</code> 匹配任意的空白符，包括空格，制表符(Tab)，换行符，中文全角空格等。</li> <li><code>\w</code> 匹配字母、数字或下划线。</li></ul> <p>下面来看看更多的例子：</p> <p><code>\ba\w*\b</code> 匹配以字母 a 开头的单词</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>animation, transition, all, family</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\ba\w*\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">animation</span>, transition, <span class="code-active">all</span>, family</code></pre></div></section></div> <p><code>\d+</code> 匹配 1 个或更多连续的数字</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>123, transition, 02, family, 1</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\d+</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">123</span>, transition, <span class="code-active">02</span>, family, <span class="code-active">1</span></code></pre></div></section></div> <blockquote><p>这里的 <code>+</code> 是和 <code>*</code> 类似的元字符，不同的是 <code>*</code> 匹配重复任意次（可能是 0 次），而 <code>+</code> 则匹配重复 1 次或更多次。</p></blockquote> <p><code>\b\w{6}\b</code> 匹配刚好六个字符的单词</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>typeof, start, name, family, jk, 烟火里的尘埃, 小情歌</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b\w{6}\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">typeof</span>, start, name, <span class="code-active">family</span>, jk, 烟火里的尘埃, 小情歌</code></pre></div></section></div> <h3 id="常用的元字符"><a href="#常用的元字符" class="header-anchor">#</a> 常用的元字符</h3> <table><thead><tr><th>代码</th> <th>说明</th></tr></thead> <tbody><tr><td><code>.</code></td> <td>匹配除换行符以外任意字符</td></tr> <tr><td><code>\w</code></td> <td>匹配字母、数字或下划线</td></tr> <tr><td><code>\s</code></td> <td>匹配任意的空白符</td></tr> <tr><td><code>\d</code></td> <td>匹配数字</td></tr> <tr><td><code>\b</code></td> <td>匹配单词的开始或结束</td></tr> <tr><td><code>^</code></td> <td>匹配字符串的开始</td></tr> <tr><td><code>$</code></td> <td>匹配字符串的结束</td></tr></tbody></table> <p>元字符 <code>^</code>（和数字 6 在同一个键位上的符号）和 <code>$</code> 都匹配一个位置，这和 <code>\b</code> 有点类似。<code>^</code> 匹配你要用来查找的字符串的开头，<code>$</code> 匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的 QQ 号必须为 5 位到 12 位数字时，可以使用：<code>^\d{5,12}$</code>，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>12345</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>^\d{5,12}$</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">12345</span></code></pre></div></section></div> <blockquote><p>这里的 <code>{5,12}</code> 表示重复的次数不能少于 5 次，不能多于 12 次，否则都不匹配。</p></blockquote> <p>它们（<code>^</code>、<code>$</code>）和 <code>\b</code> 最大的区别就是，不能多次重复，必须是一段完整字符串的开头和结尾，假如要匹配一段字符串中的内容，还是需要 <code>\b</code>，如下：</p> <p>使用 <code>^</code>、<code>$</code></p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>12345, 000, 1234567, 222, r12345, 12345t67</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>^\d{5,12}$</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>12345, 000, 1234567, 222, r12345, 12345t67</code></pre></div></section></div> <p>使用 <code>\b</code></p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>12345, 000, 1234567, 222, r12345, 12345t67</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b\d{5,12}\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">12345</span>, 000, <span class="code-active">1234567</span>, 222, r12345, 12345t67</code></pre></div></section></div> <h2 id="字符转义"><a href="#字符转义" class="header-anchor">#</a> 字符转义</h2> <p>如果你想查找元字符本身的话，比如你查找 <code>.</code>，或者 <code>*</code>，就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用 <code>\</code> 来取消这些字符的特殊意义。因此，你应该使用 <code>\.</code> 和 <code>\*</code>。当然，要查找 <code>\</code> 本身，你也得用 <code>\\</code>.</p> <p>例如：<code>deerchao\.cn</code> 匹配 <code>deerchao.cn</code>，<code>C:\\Windows</code> 匹配 <code>C:\Windows</code></p> <h2 id="重复"><a href="#重复" class="header-anchor">#</a> 重复</h2> <p>你已经看过了前面的 <code>*</code>，<code>+</code>，<code>{2}</code>，<code>{5,12}</code> 这几个匹配重复的方式了。下面是正则表达式中所有的限定符</p> <h3 id="常用的限定符"><a href="#常用的限定符" class="header-anchor">#</a> 常用的限定符</h3> <table><thead><tr><th>代码/语法</th> <th>说明</th></tr></thead> <tbody><tr><td><code>*</code></td> <td>重复零次或更多次</td></tr> <tr><td><code>+</code></td> <td>重复一次或更多次</td></tr> <tr><td><code>?</code></td> <td>重复零次或一次</td></tr> <tr><td><code>{n}</code></td> <td>重复 n 次</td></tr> <tr><td><code>{n,}</code></td> <td>重复 n 次或更多次</td></tr> <tr><td><code>{n,m}</code></td> <td>重复 n 到 m 次</td></tr></tbody></table> <p>下面是一些使用重复的例子：</p> <p><code>Windows\d+</code> 匹配 Windows 后面跟 1 个或更多数字</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>WindowsXP
Windows7
Windows10</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>Windows\d+</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>WindowsXP
<span class="code-active">Windows7</span>
<span class="code-active">Windows10</span></code></pre></div></section></div> <p><code>^\w+</code> 匹配一行的第一个单词（或整个字符串的第一个单词，具体匹配哪个意思得看选项设置）</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Just to find,to find my samurai.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>^\w+</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">Just</span> to find,to find my samurai.</code></pre></div></section></div> <h2 id="字符类"><a href="#字符类" class="header-anchor">#</a> 字符类</h2> <p>要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？</p> <p>很简单，你只需要在方括号里列出它们就行了，比如 <code>[aeiou]</code> 就匹配任何一个英文元音字母</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Sinister faces, growing curses.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>[aeiou]</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>S<span class="code-active">i</span>n<span class="code-active">i</span>st<span class="code-active">e</span>r f<span class="code-active">a</span>c<span class="code-active">e</span>s, gr<span class="code-active">o</span>w<span class="code-active">i</span>ng c<span class="code-active">u</span>rs<span class="code-active">e</span>s.</code></pre></div></section></div> <p><code>[.?!]</code> 匹配标点符号(<code>.</code> 或 <code>?</code> 或 <code>!</code>)。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Who are you?
Where are you from?
where are you going?
My name is Tang Sanzang!
Coming from Dongtu Datang.
Go to the West to learn the scriptures.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>[.?!]</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>Who are you<span class="code-active">?</span>
Where are you from<span class="code-active">?</span>
where are you going<span class="code-active">?</span>
My name is Tang Sanzang<span class="code-active">!</span>
Coming from Dongtu Datang<span class="code-active">.</span>
Go to the West to learn the scriptures<span class="code-active">.</span></code></pre></div></section></div> <p>我们也可以轻松地指定一个字符范围，像 <code>[0-9]</code> 代表的含意与 <code>\d</code> 就是完全一致的：一位数字；同理 <code>[a-z0-9A-Z_]</code> 也完全等同于 <code>\w</code>（在 JavaScript 中 <code>\w</code> 不包括汉字，但其他语言可能有所不同）。</p> <p>下面看一个更复杂的表达式：<code>\(?0\d{2}[) -]?\d{8}</code>。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>(010)88886666, 452-19980000, 022-22334455, 1721234567, 02912345678</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\(?0\d{2}[) -]?\d{8}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">(010)88886666</span>, 452-19980000, <span class="code-active">022-22334455</span>, 1721234567, <span class="code-active">02912345678</span></code></pre></div></section></div> <p>这个表达式可以匹配几种格式的电话号码，像 <code>(010)88886666</code>，或 <code>022-22334455</code>，或 <code>02912345678</code> 等。我们对它进行一些分析吧：首先是一个转义字符 <code>\(</code>，它能出现 0 次或 1 次 <code>?</code> ，然后是一个 0，后面跟着 2 个数字 <code>\d{2}</code>，然后是 <code>)</code> 或 <code>-</code> 或 空格 中的一个，它出现 1 次或不出现 <code>?</code>，最后是 8 个数字 <code>\d{8}</code> 。</p> <h2 id="分枝条件"><a href="#分枝条件" class="header-anchor">#</a> 分枝条件</h2> <p>不幸的是，刚才那个表达式也能匹配 <code>010)12345678</code> 或 <code>(022-87654321</code> 这样的“不正确”的格式。要解决这个问题，我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用 <code>|</code> 把不同的规则分隔开。听不明白？没关系，看例子：</p> <p><code>0\d{2}-\d{8}|0\d{3}-\d{7}</code> 这个表达式能匹配两种以连字号分隔的电话号码：一种是 3 位区号，8 位本地号（如<code>010-12345678</code>），一种是 4 位区号，7 位本地号（<code>0376-2233445</code>）。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>010-12345678, 452-19980000, 0376-2233445, 1721234567</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>0\d{2}-\d{8}|0\d{3}-\d{7}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">010-12345678</span>, 452-19980000, <span class="code-active">0376-2233445</span>, 1721234567</code></pre></div></section></div> <p><code>\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}</code> 这个表达式匹配 3 位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持 4 位区号的。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>010-12345678, 010 12345678, (010)12345678, (010) 12345678</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">010-12345678</span>, <span class="code-active">010 12345678</span>, <span class="code-active">(010)12345678</span>, <span class="code-active">(010) 12345678</span></code></pre></div></section></div> <p>PS：🤔 那我来试试 4 位区号</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>010-12345678, 010 12345678, (010)12345678, (010) 12345678
0222-1234567, 0222 1234567, (0222)1234567, (0222) 1234567</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}|\(0\d{3}\)[- ]?\d{7}|0\d{3}[- ]?\d{7}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">010-12345678</span>, <span class="code-active">010 12345678</span>, <span class="code-active">(010)12345678</span>, <span class="code-active">(010) 12345678</span>
<span class="code-active">0222-1234567</span>, <span class="code-active">0222 1234567</span>, <span class="code-active">(0222)1234567</span>, <span class="code-active">(0222) 1234567</span></code></pre></div></section></div> <p><code>\d{5}-\d{4}|\d{5}</code> 这个表达式用于匹配美国的邮政编码。美国邮编的规则是 5 位数字，或者用连字号间隔的 9 位数字。之所以要给出这个例子是因为它能说明一个问题：使用分枝条件时，要注意各个条件的顺序。如果你把它改成 <code>\d{5}|\d{5}-\d{4}</code> 的话，那么就只会匹配 5 位的邮编（以及 9 位邮编的前 5 位）。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。如下</p> <p>正确用法：</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>12345-1234, 12345</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\d{5}-\d{4}|\d{5}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">12345-1234</span>, <span class="code-active">12345</span></code></pre></div></section></div> <p>错误用法（顺序错误）：</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>12345-1234, 12345</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\d{5}|\d{5}-\d{4}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">12345</span>-1234, <span class="code-active">12345</span></code></pre></div></section></div> <blockquote><p>关于错误用法：其实就是，如果先匹配了前五位，后面匹配规则就从 <code>-</code> 开始了，所以自然也没有满足 9 位邮编的了。</p></blockquote> <h2 id="分组"><a href="#分组" class="header-anchor">#</a> 分组</h2> <p>我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定子表达式(也叫做分组)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。</p> <p><code>(\d{1,3}\.){3}\d{1,3}</code> 是一个简单的IP地址匹配表达式</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>192.168.123.123, 192.001.002,</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>(\d{1,3}\.){3}\d{1,3}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">192.168.123.123</span>, 192.001.002,</code></pre></div></section></div> <p>要理解这个表达式也不难，按照以下顺序分析：<code>\d{1,3}</code> 匹配 1 到 3 位的数字，<code>(\d{1,3}\.){3}</code> 匹配三位数字加上一个英文句号（这个整体也就是这个分组）重复 3 次，最后再加上一个一到三位的数字 <code>\d{1,3}</code> 。</p> <p>不幸的是，它也将匹配 256.300.888.999 这种不可能存在的IP地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的 IP 地址：</p> <div class="language-regex extra-class"><pre class="language-regex"><code><span class="token group punctuation">(</span><span class="token group punctuation">(</span>2<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>4</span><span class="token charset-punctuation punctuation">]</span></span><span class="token charclass class-name">\d</span><span class="token alternation keyword">|</span>25<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>5</span><span class="token charset-punctuation punctuation">]</span></span><span class="token alternation keyword">|</span><span class="token charset"><span class="token charset-punctuation punctuation">[</span>01<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">?</span><span class="token charclass class-name">\d</span><span class="token charclass class-name">\d</span><span class="token quantifier number">?</span><span class="token group punctuation">)</span><span class="token special-escape escape">\.</span><span class="token group punctuation">)</span><span class="token quantifier number">{3}</span><span class="token group punctuation">(</span>2<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>4</span><span class="token charset-punctuation punctuation">]</span></span><span class="token charclass class-name">\d</span><span class="token alternation keyword">|</span>25<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>5</span><span class="token charset-punctuation punctuation">]</span></span><span class="token alternation keyword">|</span><span class="token charset"><span class="token charset-punctuation punctuation">[</span>01<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">?</span><span class="token charclass class-name">\d</span><span class="token charclass class-name">\d</span><span class="token quantifier number">?</span><span class="token group punctuation">)</span>
</code></pre></div><p>理解这个表达式的关键是理解 <code>2[0-4]\d|25[0-5]|[01]?\d\d?</code>，这里我就不细说了，你自己应该能分析得出来它的意义。</p> <blockquote><p>IP 地址中每个数字都不能大于 255. 经常有人问我， 01.02.03.04 这样前面带有 0 的数字, 是不是正确的 IP 地址呢？答案是: 是的， IP 地址里的数字可以包含有前导 0 (leading zeroes)。</p></blockquote> <h2 id="反义"><a href="#反义" class="header-anchor">#</a> 反义</h2> <p>有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到反义：</p> <h3 id="常用的反义代码"><a href="#常用的反义代码" class="header-anchor">#</a> 常用的反义代码</h3> <table><thead><tr><th>代码/语法</th> <th>说明</th></tr></thead> <tbody><tr><td><code>\W</code></td> <td>匹配任意不是字母，数字，下划线，汉字的字符</td></tr> <tr><td><code>\S</code></td> <td>匹配任意不是空白符的字符</td></tr> <tr><td><code>\D</code></td> <td>匹配任意非数字的字符</td></tr> <tr><td><code>\B</code></td> <td>匹配不是单词开头或结束的位置</td></tr> <tr><td><code>[^x]</code></td> <td>匹配除了x以外的任意字符</td></tr> <tr><td><code>[^aeiou]</code></td> <td>匹配除了 aeiou 这几个字母以外的任意字符</td></tr></tbody></table> <p>例子：</p> <p>例1：<code>\S+</code> 匹配不包含空白符的字符串</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>async await try cache</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\S+</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">async</span> <span class="code-active">await</span> <span class="code-active">try</span> <span class="code-active">cache</span></code></pre></div></section></div> <p>例2：<code>&lt;a[^&gt;]+&gt;</code> 匹配用尖括号括起来的以 a 开头的字符串。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>&lt;article&gt;, article, &lt;audio&gt;, hallo, &lt;a&gt;</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>&lt;a[^&gt;]+&gt;</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">&lt;article&gt;</span>, article, <span class="code-active">&lt;audio&gt;</span>, hallo, &lt;a&gt;</code></pre></div></section></div> <h2 id="后向引用"><a href="#后向引用" class="header-anchor">#</a> 后向引用</h2> <p>使用小括号指定一个子表达式后，匹配这个子表达式的文本（也就是此分组捕获的内容）可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个组号，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为 1，第二个为 2，以此类推。</p> <div class="custom-block info"><p class="custom-block-title">呃… 其实，组号分配还不像我刚说得那么简单：</p><ul><li>分组 0 对应整个正则表达式。</li> <li>实际上组号分配过程是要从左向右扫描两遍的：第一遍只给未命名组分配，第二遍只给命名组分配，因此所有命名组的组号都大于未命名的组号。</li> <li>你可以使用 <code>(?:exp)</code> 这样的语法来剥夺一个分组对组号分配的参与权。</li></ul></div><p>后向引用用于重复搜索前面某个分组匹配的文本。例如，<code>\1</code> 代表分组 1 匹配的文本。难以理解？请看示例：</p> <p><code>\b(\w+)\b\s+\1\b</code> 可以用来匹配重复的单词，像 go go, 或者 kitty kitty，如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>go go, kitty kitty, give me</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b(\w+)\b\s+\1\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">go go</span>, <span class="code-active">kitty kitty</span>, give me</code></pre></div></section></div> <p>这个表达式首先是一个单词，也就是单词开始处和结束处之间的多于一个的字母或数字 <code>\b(\w+)\b</code>，这个单词会被捕获到编号为 1 的分组中，然后是 1 个或几个空白符 <code>\s+</code>，最后是分组 1 中捕获的内容（也就是前面匹配的那个单词）<code>\1</code>。</p> <p>你也可以自己指定子表达式的组名。要指定一个子表达式的组名，请使用这样的语法：<code>(?&lt;Word&gt;\w+)</code> （或者把尖括号换成 <code>'</code> 也行：<code>(?'Word'\w+)</code>），这样就把 <code>\w+</code> 的组名指定为 Word 了。要反向引用这个分组捕获的内容，你可以使用 <code>\k&lt;Word&gt;</code> ，所以上一个例子也可以写成这样：</p> <div class="language-regex extra-class"><pre class="language-regex"><code><span class="token anchor function">\b</span><span class="token group punctuation">(?&lt;<span class="token group-name variable">Word</span>&gt;</span><span class="token charclass class-name">\w</span><span class="token quantifier number">+</span><span class="token group punctuation">)</span><span class="token anchor function">\b</span><span class="token charclass class-name">\s</span><span class="token quantifier number">+</span><span class="token backreference keyword">\k&lt;<span class="token group-name variable">Word</span>&gt;</span><span class="token anchor function">\b</span>
</code></pre></div><p>示例如下</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>go go, kitty kitty, give me</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b(?&lt;Word&gt;\w+)\b\s+\k&lt;Word&gt;\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">go go</span>, <span class="code-active">kitty kitty</span>, give me</code></pre></div></section></div> <p>使用小括号的时候，还有很多特定用途的语法。下面列出了最常用的一些：</p> <h3 id="常用分组语法"><a href="#常用分组语法" class="header-anchor">#</a> 常用分组语法</h3> <table cellspacing="0"><tbody class="regex-table-tbody"><tr><th scope="col">分类</th> <th scope="col">代码/语法</th> <th scope="col">说明</th></tr> <tr><th rowspan="3">捕获</th> <td><code class="code-word">(exp)</code></td> <td><span class="desc">匹配 exp，并捕获文本到自动命名的组里。</span></td></tr> <tr><td><code class="code-word">(?&lt;name&gt;exp)</code></td> <td><span class="desc">匹配 exp，并捕获文本到名称为 name 的组里，也可以写成 <code class="code-word">(?'name'exp)</code></span>。</td></tr> <tr><td><code class="code-word">(?:exp)</code></td> <td><span class="desc">匹配 exp，不捕获匹配的文本，也不给此分组分配组号。</span></td></tr> <tr><th rowspan="4">零宽断言</th> <td><code class="code-word">(?=exp)</code></td> <td><span class="desc">匹配 exp 前面的位置。</span></td></tr> <tr><td><code class="code-word">(?&lt;=exp)</code></td> <td><span class="desc">匹配 exp 后面的位置。</span></td></tr> <tr><td><code class="code-word">(?!exp)</code></td> <td><span class="desc">匹配后面跟的不是 exp 的位置。</span></td></tr> <tr><td><code class="code-word">(?&lt;!exp)</code></td> <td><span class="desc">匹配前面不是 exp 的位置。</span></td></tr> <tr><th rowspan="1">注释</th> <td><code class="code-word">(?#comment)</code></td> <td><span class="desc">这种类型的分组不对正则表达式的处理产生任何影响，用于提供注释让人阅读。</span></td></tr></tbody></table> <p>我们已经讨论了前两种语法。第三个 <code>(?:exp)</code> 不会改变正则表达式的处理方式，只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面，也不会拥有组号。“我为什么会想要这样做？”—— 好问题，你觉得为什么呢？</p> <h2 id="零宽断言"><a href="#零宽断言" class="header-anchor">#</a> 零宽断言</h2> <blockquote><p>断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。</p></blockquote> <p>接下来的四个用于查找在某些内容（但并不包括这些内容）之前或之后的东西，也就是说它们像 <code>\b</code>, <code>^</code>, <code>$</code> 那样用于指定一个位置，这个位置应该满足一定的条件（即断言），因此它们也被称为零宽断言。最好还是拿例子来说明吧：</p> <p><code>(?=exp)</code> 也叫<strong>零宽度正预测先行断言</strong>，它断言自身出现的位置的后面能匹配表达式 exp。</p> <p>比如 <code>\b\w+(?=ing\b)</code>，匹配以 ing 结尾的单词的前面部分（除了 ing 以外的部分），假如查找 I'm singing while you're dancing. 它会匹配 sing 和 danc。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>I'm singing while you're dancing.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b\w+(?=ing\b)</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>I'm <span class="code-active">sing</span>ing while you're <span class="code-active">danc</span>ing.</code></pre></div></section></div> <p><code>(?&lt;=exp)</code> 也叫<strong>零宽度正回顾后发断言</strong>，它断言自身出现的位置的前面能匹配表达式 exp。</p> <p>比如 <code>(?&lt;=\bre)\w+\b</code> 会匹配以 re 开头的单词的后半部分（除了 re 以外的部分），例如在查找 reading a book 时，它匹配 ading。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>reading a book</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>(?&lt;=\bre)\w+\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>re<span class="code-active">ading</span> a book</code></pre></div></section></div> <p>假如你要给一个很长的数字中每三位间加一个逗号（当然是从右边加起了），你可以这样查找需要在前面和里面添加逗号的部分：<code>((?&lt;=\d)\d{3})+\b</code>，用它对 1234567890 进行查找时结果是 234567890 。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>1234567890</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>((?&lt;=\d)\d{3})+\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>1<span class="code-active">234567890</span></code></pre></div></section></div> <p>下面这个例子同时使用了这两种断言：</p> <p><code>(?&lt;=\s)\d+(?=\s)</code> 匹配以空白符间隔的数字（再次强调，不包括这些空白符）。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>1 2 3 4 5, 123, 111 222 333 444 555</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>(?&lt;=\s)\d+(?=\s)</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>1 <span class="code-active">2</span> <span class="code-active">3</span> <span class="code-active">4</span> 5, 123, <span class="code-active">111</span> <span class="code-active">222</span> <span class="code-active">333</span> <span class="code-active">444</span> 555</code></pre></div></section></div> <h2 id="负向零宽断言"><a href="#负向零宽断言" class="header-anchor">#</a> 负向零宽断言</h2> <p>前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法（反义）。但是如果我们只是想要确保某个字符没有出现，但并不想去匹配它时怎么办？例如，如果我们想查找这样的单词，它里面出现了字母 q，但是 q 后面跟的不是字母 u，我们可以尝试这样：</p> <p><code>\b\w*q[^u]\w*\b</code> 匹配包含后面不是字母 u 的字母 q 的单词。但是如果多做测试（或者你思维足够敏锐，直接就观察出来了），你会发现，如果 q 出现在单词的结尾的话，像匹配 <code>Iraq,Benq</code>，这个表达式就会出错。这是因为 <code>[^u]</code> 总要匹配一个字符，所以如果 q 是单词的最后一个字符的话，后面的 <code>[^u]</code> 将会匹配 q 后面的单词分隔符（可能是空格，或者是句号或其它的什么），后面的 <code>\w*\b</code> 将会匹配下一个单词，于是 <code>\b\w*q[^u]\w*\b</code> 就会匹配整个 <code>Iraq fighting</code>。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Iraq,Benq. Iraq fighting.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b\w*q[^u]\w*\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">Iraq,Benq</span>. <span class="code-active">Iraq fighting</span>.</code></pre></div></section></div> <p><strong>负向零宽断言</strong> 能解决这样的问题，因为它只匹配一个位置，并不消费任何字符。现在，我们可以这样来解决这个问题：<code>\b\w*q(?!u)\w*\b</code>。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Iraq,Benq. Iraq fighting.</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b\w*q(?!u)\w*\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">Iraq</span>,<span class="code-active">Benq</span>. <span class="code-active">Iraq</span> fighting.</code></pre></div></section></div> <p><strong>零宽度负预测先行断言</strong> <code>(?!exp)</code>，断言此位置的后面不能匹配表达式 exp。例如：</p> <p><code>\d{3}(?!\d)</code> 匹配三位数字，而且这三位数字的后面不能是数字；</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>123 1 1234 12 12345</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\d{3}(?!\d)</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">123</span> 1 1<span class="code-active">234</span> 12 12<span class="code-active">345</span></code></pre></div></section></div> <p><code>\b((?!abc)\w)+\b</code> 匹配不包含连续字符串 abc 的单词。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>Yea isn't that the best thing? abc, abcde</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>\b((?!abc)\w)+\b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">Yea</span> <span class="code-active">isn</span>'<span class="code-active">t</span> <span class="code-active">that</span> <span class="code-active">the</span> <span class="code-active">best</span> <span class="code-active">thing</span>? abc, abcde</code></pre></div></section></div> <p>同理，我们可以用 <code>(?&lt;!exp)</code> ，<strong>零宽度负回顾后发断言</strong> 来断言此位置的前面不能匹配表达式 exp：<code>(?&lt;![a-z])\d{7}</code> 匹配前面不是小写字母的七位数字。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>R1234567, r1234567, 1234567, 12345678, r12345678</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>(?&lt;![a-z])\d{7}</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>R<span class="code-active">1234567</span>, r1234567, <span class="code-active">1234567</span>, <span class="code-active">1234567</span>8, r1<span class="code-active">2345678</span></code></pre></div></section></div> <p>一个更复杂的例子：<code>(?&lt;=&lt;(\w+)&gt;).*(?=&lt;\/\1&gt;)</code> 匹配不包含属性的简单 HTML 标签内里的内容。<code>(?&lt;=&lt;(\w+)&gt;)</code> 指定了这样的前缀：被尖括号括起来的单词（比如可能是 <code>&lt;b&gt;</code>），然后是 <code>.*</code> （任意的字符串），最后是一个后缀 <code>(?=&lt;\/\1&gt;)</code>。注意后缀里的 <code>\/</code>，它用到了前面提过的字符转义；<code>\1</code> 则是一个反向引用，引用的正是捕获的第一组，前面的 <code>(\w+)</code> 匹配的内容。这样如果前缀实际上是 <code>&lt;b&gt;</code> 的话，后缀就是 <code>&lt;/b&gt;</code> 了。整个表达式匹配的是 <code>&lt;b&gt;</code> 和 <code>&lt;/b&gt;</code> 之间的内容（再次提醒，不包括前缀和后缀本身）。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>&lt;b&gt;hello&lt;/b&gt;</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>(?&lt;=&lt;(\w+)&gt;).*(?=&lt;\/\1&gt;)</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code>&lt;b&gt;<span class="code-active">hello</span>&lt;/b&gt;</code></pre></div></section></div> <h2 id="注释"><a href="#注释" class="header-anchor">#</a> 注释</h2> <p>小括号的另一种用途是通过语法 <code>(?#comment)</code> 来包含注释。例如：</p> <div class="language-regex extra-class"><pre class="language-regex"><code>2<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>4</span><span class="token charset-punctuation punctuation">]</span></span><span class="token charclass class-name">\d</span><span class="token group punctuation">(</span><span class="token quantifier number">?</span>#200-249<span class="token group punctuation">)</span><span class="token alternation keyword">|</span>25<span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token range">0<span class="token range-punctuation operator">-</span>5</span><span class="token charset-punctuation punctuation">]</span></span><span class="token group punctuation">(</span><span class="token quantifier number">?</span>#250-255<span class="token group punctuation">)</span><span class="token alternation keyword">|</span><span class="token charset"><span class="token charset-punctuation punctuation">[</span>01<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">?</span><span class="token charclass class-name">\d</span><span class="token charclass class-name">\d</span><span class="token quantifier number">?</span><span class="token group punctuation">(</span><span class="token quantifier number">?</span>#0-199<span class="token group punctuation">)</span>
</code></pre></div><p>要包含注释的话，最好是启用“忽略模式里的空白符”选项，这样在编写表达式时能任意的添加空格，Tab，换行，而实际使用时这些都将被忽略。启用这个选项后，在 <code>#</code> 后面到这一行结束的所有文本都将被当成注释忽略掉。例如，我们可以前面的一个表达式写成这样：</p> <div class="language-regex extra-class"><pre class="language-regex"><code><span class="token group punctuation">(?&lt;=</span>    # 断言要匹配的文本的前缀
&lt;<span class="token group punctuation">(</span><span class="token charclass class-name">\w</span><span class="token quantifier number">+</span><span class="token group punctuation">)</span>&gt; # 查找尖括号括起来的内容
        # <span class="token group punctuation">(</span>即HTML/XML标签<span class="token group punctuation">)</span>
<span class="token group punctuation">)</span>       # 前缀结束
<span class="token charclass class-name">.</span><span class="token quantifier number">*</span>      # 匹配任意文本
<span class="token group punctuation">(?=</span>     # 断言要匹配的文本的后缀
&lt;<span class="token escape">\/</span><span class="token backreference keyword">\1</span>&gt;  # 查找尖括号括起来的内容
        # 查找尖括号括起来的内容
<span class="token group punctuation">)</span>       # 后缀结束
</code></pre></div><h2 id="贪婪与懒惰"><a href="#贪婪与懒惰" class="header-anchor">#</a> 贪婪与懒惰</h2> <p>当正则表达式中包含能接受重复的限定符时，通常的行为是（在使整个表达式能得到匹配的前提下）匹配尽可能多的字符。以这个表达式为例：<code>a.*b</code>，它将会匹配最长的以 a 开始，以 b 结束的字符串。如果用它来搜索 aabcab 的话，它会匹配整个字符串 aabcab。这被称为贪婪匹配。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>aabcab</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>a.*b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">aabcab</span></code></pre></div></section></div> <p>有时，我们更需要懒惰匹配，也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式，只要在它后面加上一个问号 <code>?</code>。这样 <code>.*?</code> 就意味着匹配任意数量的重复，但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧：</p> <p><code>a.*?b</code> 匹配最短的，以 a 开始，以 b 结束的字符串。如果把它应用于 aabcab 的话，它会匹配 aab（第一到第三个字符）和 ab（第五到第六个字符）。</p> <div class="regex-case re-part"><section class="regex-case-part"><p class="regex-case-part__title">文本内容</p> <div class="regex-case-part__code"><pre><code>aabcab</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">正则规则</p> <div class="regex-case-part__code"><pre><code>a.*?b</code></pre></div></section> <section class="regex-case-part"><p class="regex-case-part__title">匹配结果</p> <div class="regex-case-part__code"><pre><code><span class="code-active">aab</span>c<span class="code-active">ab</span></code></pre></div></section></div> <blockquote><p>为什么第一个匹配是 aab（第一到第三个字符）而不是 ab（第二到第三个字符）？简单地说，因为正则表达式有另一条规则，比懒惰／贪婪规则的优先级更高：最先开始的匹配拥有最高的优先权——The match that begins earliest wins。</p></blockquote> <h3 id="懒惰限定符"><a href="#懒惰限定符" class="header-anchor">#</a> 懒惰限定符</h3> <table><thead><tr><th>代码/语法</th> <th>说明</th></tr></thead> <tbody><tr><td><code>*?</code></td> <td>重复任意次，但尽可能少重复。</td></tr> <tr><td><code>+?</code></td> <td>重复 1 次或更多次，但尽可能少重复。</td></tr> <tr><td><code>??</code></td> <td>重复 0 次或 1 次，但尽可能少重复。</td></tr> <tr><td><code>{n,m}?</code></td> <td>重复 n 到 m 次，但尽可能少重复。</td></tr> <tr><td><code>{n,}?</code></td> <td>重复 n 次以上，但尽可能少重复。</td></tr></tbody></table> <h2 id="处理选项"><a href="#处理选项" class="header-anchor">#</a> 处理选项</h2> <p>上面介绍了几个选项如忽略大小写，处理多行等，这些选项能用来改变处理正则表达式的方式。下面是 <code>.Net</code> 中常用的正则表达式选项：</p> <blockquote><p>C# 中，你可以使用 <code>Regex(String, RegexOptions)</code> 构造函数来设置正则表达式的处理选项。如：<code>Regex regex = new Regex(@&quot;\ba\w{6}\b&quot;, RegexOptions.IgnoreCase);</code></p></blockquote> <h3 id="常用的处理选项"><a href="#常用的处理选项" class="header-anchor">#</a> 常用的处理选项</h3> <table><thead><tr><th>名称</th> <th>说明</th></tr></thead> <tbody><tr><td>IgnoreCase（忽略大小写）</td> <td>匹配时不区分大小写。</td></tr> <tr><td>Multiline（多行模式）</td> <td>更改 <code>^</code> 和 <code>$</code> 的含义，使它们分别在任意一行的行首和行尾匹配，而不仅仅在整个字符串的开头和结尾匹配。（在此模式下，<code>$</code> 的精确含意是：匹配 <code>\n</code> 之前的位置以及字符串结束前的位置）</td></tr> <tr><td>Singleline（单行模式）</td> <td>更改 <code>.</code> 的含义，使它与每一个字符匹配（包括换行符 <code>\n</code>）。</td></tr> <tr><td>IgnorePatternWhitespace（忽略空白）</td> <td>忽略表达式中的非转义空白并启用由 <code>#</code> 标记的注释。</td></tr> <tr><td>ExplicitCapture（显式捕获）</td> <td>仅捕获已被显式命名的组。</td></tr></tbody></table> <p>一个经常被问到的问题是：是不是只能同时使用多行模式和单行模式中的一种？答案是：不是。这两个选项之间没有任何关系，除了它们的名字比较相似（以至于让人感到疑惑）以外。事实上，为了避免混淆，在最新的 JavaScript 中，单行模式其实名叫 dotAll，意为点可以匹配所有字符，然而在指定该选项时，用的还是 Singleline 的首字母 s 。</p> <blockquote><p>目前（2019/06），只有基于 Webkit/Chromium 的浏览器（如 Chrome，Safari等）才支持 dotAll 选项。</p></blockquote> <h2 id="平衡组-递归匹配"><a href="#平衡组-递归匹配" class="header-anchor">#</a> 平衡组/递归匹配</h2> <p>有时我们需要匹配像 <code>( 100 * ( 50 + 15 ) )</code> 这样的可嵌套的层次性结构，这时简单地使用 <code>\(.+\)</code> 则只会匹配到最左边的左括号和最右边的右括号之间的内容（这里我们讨论的是贪婪模式，懒惰模式也有下面的问题）。假如原来的字符串里的左括号和右括号出现的次数不相等，比如 <code>( 5 / ( 3 + 2 ) ) )</code>，那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的，配对的括号之间的内容呢？</p> <p>为了避免 <code>(</code> 和 <code>\(</code> 把你的大脑彻底搞糊涂，我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把 <code>xx &lt;aa &lt;bbb&gt; &lt;bbb&gt; aa&gt; yy</code> 这样的字符串里，最长的配对的尖括号内的内容捕获出来？</p> <p>这里需要用到以下的语法构造：</p> <ul><li><code>(?'group')</code> 把捕获的内容命名为 group，并压入 <strong>堆栈(Stack)</strong>。</li> <li><code>(?'-group')</code> 从堆栈上弹出最后压入堆栈的名为 group 的捕获内容，如果堆栈本来为空，则本分组的匹配失败。</li> <li><code>(?(group)yes|no)</code> 如果堆栈上存在以名为 group 的捕获内容的话，继续匹配 yes 部分的表达式，否则继续匹配 no 部分。</li> <li><code>(?!)</code> 零宽负向先行断言，由于没有后缀表达式，试图匹配总是失败。</li></ul> <p>我们需要做的是每碰到了左括号，就在压入一个&quot;Open&quot;,每碰到一个右括号，就弹出一个，到了最后就看看堆栈是否为空，如果不为空那就证明左括号比右括号多，那匹配就应该失败。正则表达式引擎会进行回溯（放弃最前面或最后面的一些字符），尽量使整个表达式得到匹配。</p> <div class="language-regex extra-class"><pre class="language-regex"><code>&lt;                    #最外层的左括号
  <span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token charset-negation operator">^</span>&lt;&gt;<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">*</span>             #它后面非括号的内容
  <span class="token group punctuation">(</span>
      <span class="token group punctuation">(</span>
        <span class="token group punctuation">(?'<span class="token group-name variable">Open</span>'</span>&lt;<span class="token group punctuation">)</span>   #左括号，压入&quot;Open&quot;
        <span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token charset-negation operator">^</span>&lt;&gt;<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">*</span>       #左括号后面的内容
      <span class="token group punctuation">)</span><span class="token quantifier number">+</span>
      <span class="token group punctuation">(</span>
        <span class="token group punctuation">(?'<span class="token group-name variable">-Open</span>'</span>&gt;<span class="token group punctuation">)</span>  #右括号，弹出一个&quot;Open&quot;
        <span class="token charset"><span class="token charset-punctuation punctuation">[</span><span class="token charset-negation operator">^</span>&lt;&gt;<span class="token charset-punctuation punctuation">]</span></span><span class="token quantifier number">*</span>       #右括号后面的内容
      <span class="token group punctuation">)</span><span class="token quantifier number">+</span>
  <span class="token group punctuation">)</span><span class="token quantifier number">*</span>
  <span class="token group punctuation">(</span><span class="token quantifier number">?</span><span class="token group punctuation">(</span>Open<span class="token group punctuation">)</span><span class="token group punctuation">(?!</span><span class="token group punctuation">)</span><span class="token group punctuation">)</span>      #最外层的右括号前检查
                     #若还有未弹出的&quot;Open&quot;
                     #则匹配失败

&gt;                    #最外层的右括号
</code></pre></div></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">上次更新：</span><span class="time">2021-03-11 11:30</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev"><svg aria-hidden="true" class="icon"><use xlink:href="#icon-left"></use></svg> <a href="/docs/devbook/" class="prev router-link-active">介绍</a></span> <span class="next"><a href="/docs/devbook/note/lisp-getting-started.html">Lisp 入门</a> <svg aria-hidden="true" class="icon"><use xlink:href="#icon-right"></use></svg></span></p></div> </main></div><div class="global-ui"><!----><!----></div></div>
    <script src="/docs/assets/js/app.cdec4db9.js" defer></script><script src="/docs/assets/js/2.993bd611.js" defer></script><script src="/docs/assets/js/9.2fa568cb.js" defer></script><script src="/docs/assets/js/3.a7f8dd77.js" defer></script><script src="/docs/assets/js/7.5a12abef.js" defer></script>
  </body>
</html>
